"""
Copyright (c) 2022 Huawei Technologies Co.,Ltd.

openGauss is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:

          http://license.coscl.org.cn/MulanPSL2

THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
"""
"""
Case Type   : 端口故障类
Case Name   : 启动单节点-主节点基础端口被占用
Create At   : 2021/07/09
Owner       : @wan005
Description :
    1.stop主节点,注入基础端口被占用故障
    gs_ctl stop -D {dn1}
    ./cfe "inject rNet_port_conflict (port) values({port})"
    2.启动主节点
    gs_ctl start -D {dn1}
    3.检查集群状态
    4.清除故障 恢复环境 再次启动
    ./cfe "clean rNet_port_conflict where port ={port}"
    gs_ctl start -D {dn1}
    gs_om -t restart
    gs_om -t status --detail
    5.运行TPCC
    6.检查数据一致性
Expect      :
    1.故障注入成功
    2.主节点启动失败
    3.数据库状态异常
    4.清除故障 恢复环境 再次启动成功
    5.运行TPCC成功
    6.数据一致
History     :
    Modified by @wan005 2021/8/4 : 优化故障清理断言和teardown
"""
import time
import unittest

from testcase.utils.ComThread import ComThread
from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.CommonSH import RestartDbCluster
from testcase.utils.Constant import Constant
from testcase.utils.Logger import Logger
from yat.test import Node, macro


class ReliabilityPort(unittest.TestCase):
    nodes = ("PrimaryDbUser", "Standby1DbUser", "Standby2DbUser")

    @RestartDbCluster(*nodes)
    def setUp(self):
        self.log = Logger()
        self.primary_sh = CommonSH(self.nodes[0])

        self.constant = Constant()
        self.com = Common()
        self.primary_node = Node(self.nodes[0])
        self.primary_root_node = Node(node="PrimaryRoot")
        self.log.info("setUp-实例化:ok")

        result = self.com.get_sh_result(self.primary_node, "nc --version")
        self.log.info(result)
        if "bash" in result:
            result = self.com.get_sh_result(self.primary_node,
                                            "yum -y install nc")
            self.log.info(result)
        self.log.info("setUp-安装nc:ok")

        self.log.info("Opengauss_Reliability_Port_Case001:开始执行")
        self.log.info("停止主节点")
        result = self.primary_sh.executeGsctl("stop", "server stopped")
        self.assertTrue(result)

        self.port = self.primary_node.db_port
        self.inject_cmd = f"rNet_port_conflict (port) values({self.port})"
        self.clean_cmd = f"rNet_port_conflict where port ={self.port}"

    def test_main(self):
        try:
            self.log.info("步骤1:主节点注入基础端口被占用故障")
            self.log.info("查看当前端口未被占用")
            shell_cmd = f"netstat -anp|grep {self.port}"
            self.log.info(shell_cmd)
            result = self.primary_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertNotIn("gaussdb", result)

            self.log.info("注入故障")
            session = ComThread(self.com.cfe_inject,
                                args=(self.primary_root_node,
                                      self.inject_cmd))
            session.setDaemon(True)
            session.start()
            time.sleep(2)

            session.join(30)
            result = session.get_result()
            self.log.info(result)

            self.log.info("注入故障后查看当前端口被占用")
            result = self.primary_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertIn(self.port, result)
            self.assertIn("0.0.0.0", result)
            self.assertIn("/nc", result)
            self.assertNotIn("gaussdb", result)

            self.log.info("步骤2:ctl启动主节点")
            result = self.primary_sh.executeGsctl("start",
                                                  f"Port {self.port} is used")
            self.log.info(result)
            self.assertTrue(result)

            self.log.info("步骤3:检查集群状态")
            db_status = self.primary_sh.getDbClusterStatus("detail")
            self.log.info(db_status)
            self.assertTrue("Unavailable" in db_status
                            and "P Down" in db_status)
        finally:
            self.log.info("步骤4:清除故障 恢复环境")
            result = self.com.cfe_clean(self.primary_root_node,
                                        self.clean_cmd)
            self.log.info(result)
            self.assertIn("successful execution", result)

            self.log.info("查看当前端口未被占用")
            shell_cmd = f"netstat -anp|grep {self.port}"
            self.log.info(shell_cmd)
            result = self.primary_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertEqual("", result.strip())

        self.log.info("再次ctl启动主节点")
        result = self.primary_sh.executeGsctl("start", "server started")
        self.assertTrue(result)

        db_status = self.primary_sh.getDbClusterStatus()
        self.log.info(db_status)
        self.assertTrue("Degraded" in db_status or "Normal" in db_status)

        self.log.info("步骤5:运行TPCC")
        result = self.com.startTPCC(self.primary_root_node, macro.TPCC_PATH)
        self.log.info(result)
        self.assertIn(self.constant.TPCC_SUCCESS_MSG, result)

        self.log.info('检查主备是否同步')
        time.sleep(10)
        self.log.info('获取节点数量')
        node_num = self.primary_sh.get_node_num()
        res = self.primary_sh.check_location_consistency("primary", node_num)
        self.assertTrue(res)

        self.log.info('步骤6:检查数据一致性')
        nodes = (self.primary_node, Node(self.nodes[1]), Node(self.nodes[2]))
        flag = self.com.check_data_sample_by_all(r'\d', *nodes)
        self.assertTrue(flag)
        table_dict = self.com.format_sql_result(
            self.primary_sh.executDbSql(r'\d'))
        table_name = table_dict.get('Name')
        for name in table_name:
            select_sql = f'select count(*) from {name};'
            flag = self.com.check_data_sample_by_all(select_sql, *nodes)
            self.assertTrue(flag)

    def tearDown(self):
        self.log.info("环境清理")
        self.com.cfe_clean(self.primary_root_node, self.clean_cmd)

        self.log.info("重启主节点")
        result = self.primary_sh.executeGsctl("start", "server started")
        self.log.info(result)

        db_status = self.primary_sh.getDbClusterStatus("detail")
        self.log.info(db_status)
        self.assertTrue("Degraded" in db_status or "Normal" in db_status)
        self.log.info("Opengauss_Reliability_Port_Case001:执行完成")
